﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace SpaceShooter
{
    public class Sprite
    {
        public Texture2D sprite;
        public Vector2 pos, vel, size, origin;
        public double angle;
        public bool alive;
        public Vector2 SCREEN_SIZE;                   

        public Sprite(Vector2 screen)
        {
            sprite = null;
            pos = new Vector2( -500.0f, -500.0f );
            vel = size = origin = Vector2.Zero;
            angle = 0.0;
            alive = false;
            SCREEN_SIZE = screen;
        }

        public Sprite(Texture2D tex, Vector2 screen)
        {
            sprite = tex;
            pos = new Vector2( -500.0f, -500.0f );
            vel = Vector2.Zero;
            size = new Vector2( (float)tex.Width, (float)tex.Height);
            origin = size / 2.0f;
            alive = false;
            SCREEN_SIZE = screen;
        }

        public Sprite(Texture2D tex, Vector2 loc, Vector2 velocity, 
                      Vector2 spriteSize, Vector2 center, double rot, 
                      bool isAlive, Vector2 screen )
        {
            sprite = tex;
            pos = loc;
            vel = velocity;
            size = spriteSize;
            origin = center;
            angle = rot;
            alive = isAlive;
            SCREEN_SIZE = screen;
        }

        public virtual int update(double elapsedTime)
        {
            pos += vel * (float)elapsedTime;
            return 0;
        }

        public virtual void draw(SpriteBatch sb)
        {
            sb.Draw(sprite, pos, new Rectangle(0, 0, (int)size.X, (int)size.Y),
                    Color.White, (float)angle, origin, 1.0f, 
                    SpriteEffects.None, 0.5f);
        }

        public bool collides(Sprite that)
        {
            if (!this.alive || !that.alive)
                return false;
            Rectangle thisBox = 
                new Rectangle((int)(pos.X - origin.X), (int)(pos.Y - origin.Y), 
                              (int)size.X, (int)size.Y);
            Rectangle thatBox =
                new Rectangle((int)(that.pos.X - that.origin.X), (int)(that.pos.Y - that.origin.Y),
                              (int)that.size.X, (int)that.size.Y);
            return thisBox.Intersects(thatBox);
        }
        public void kill()
        {
            alive = false;
            pos.X = pos.Y = -500.0f;
            vel.X = vel.Y = 0;
        }
    }

    public enum ShipState
    {
        NEUTRAL = 0,
        ACCELERATE = 1,
        DECCELERATE = 2,
        RISING = 3,
        FALLING = 4,
        EXPLODE = 5,
    }

    public class PlayerShip : Sprite
    {
        public int weaponType;
        public double fireTimer;
        public int primaryWeapon;
        public int secondaryWeapon;
        public Rectangle[] animateRect;
        public int[][] animateSequence;
        public int frameCount;
        public ShipState state;
        public double frameTimer;
        public int health;
        public int lives;
        public Texture2D healthTex;
        public Texture2D livesTex;

        public PlayerShip( Texture2D tex, Vector2 screen) : base(tex, screen) 
        {
            weaponType = 0;
            fireTimer = 0;

            state = ShipState.NEUTRAL;
            frameCount = 0;
            frameTimer = 0;
            lives = 3;
            health = 3;
            animateRect = new Rectangle[10];
            primaryWeapon = secondaryWeapon = 0;
            
            for (int i = 0; i < 10; i++)
            {
                animateRect[i] = new Rectangle(32 * i, 0, 32, 16);
            }
            animateSequence = new int[6][];
            animateSequence[0] = new int[] { 0, 1 };
            animateSequence[1] = new int[] { 0, 2 };
            animateSequence[2] = new int[] { 0, 3 };
            animateSequence[3] = new int[] { 4, 5 };
            animateSequence[4] = new int[] { 6, 7 };
            animateSequence[5] = new int[] { 8, 9 };
        }

        public override int update(double elapsedTime)
        {
            frameTimer += elapsedTime;
            if (frameTimer >= 0.66)
            {
                frameTimer -= 0.66;
                frameCount++;
                frameCount = frameCount % (animateSequence[(int)state].Length);
            }
            if (fireTimer > 0)
            {
                fireTimer -= elapsedTime;
            }

            vel *= 0.8f;

            base.update(elapsedTime);
            
            if (pos.X <= origin.X)
                pos.X = origin.X;
            if (pos.Y <= origin.Y)
                pos.Y = origin.Y;
            if (pos.X >= SCREEN_SIZE.X/2 - origin.X)
                pos.X = SCREEN_SIZE.X/2 - origin.X;
            if (pos.Y >= SCREEN_SIZE.Y - origin.Y)
                pos.Y = SCREEN_SIZE.Y - origin.Y;
            return 0;
        }

        public override void draw(SpriteBatch sb)
        {
            sb.Draw(sprite, pos, animateRect[animateSequence[(int)state][frameCount]], Color.White,
                    (float)angle, origin, 1.0f, SpriteEffects.None, 0);

            for (int i = 0; i < lives; i++)
            {
                sb.Draw(livesTex, new Rectangle((35 + i * 10 + 1), 595 - livesTex.Height, 
                    livesTex.Width, livesTex.Height), Color.Red);
            }
            sb.Draw(healthTex, new Rectangle(5, 585, health * 8, healthTex.Height), Color.Red);
        }
    }

    public class Enemy : Sprite
    {
        public int hp;
        public int flightPath;
        public double fireTimer;
        public Vector2 initPos, targetPos;
        public Enemy( Texture2D tex, Vector2 screen) : base(tex, screen) 
        {
            hp = 1;
            flightPath = 0;
            fireTimer = 0;
            targetPos = initPos = pos;
        }

        public Enemy(Texture2D tex, Vector2 screen, Vector2 loc, int health, int path) : base(tex, screen)
        {
            targetPos = initPos = pos = loc;
            hp = health;
            flightPath = path;
            fireTimer = 0;
        }

        public override int update(double elapsedTime)
        {
            if (!alive)
                return 0;
            
            fireTimer += elapsedTime;

            switch (flightPath)
            {
                case 0:
                    vel.X = -100.0f;
                    vel.Y = 0.0f;
                    if (fireTimer >= 2.0f)
                    {
                        fireTimer -= 2.0f;
                        return 1 + base.update(elapsedTime);
                    }
                    break;
                case 1:
                    vel.X = -66.6f;
                    if (pos.Y > initPos.Y)
                    {
                        vel.Y -= 10.0f;
                    }
                    if (pos.Y < initPos.Y)
                    {
                        vel.Y += 10.0f;
                    }
                    if (fireTimer >= 3.5f)
                    {
                        fireTimer -= 3.5f;
                        return 1 + base.update(elapsedTime);
                    }
                    break;
                case 2:
                    Vector2 dir = targetPos - initPos;
                    dir.Normalize();
                    angle = Math.Atan(dir.Y / dir.X);
                    if (dir.X < 0)
                        angle += Math.PI;
                    vel = dir * 105.0f;
                    if (fireTimer >= 2.0f)
                    {
                        fireTimer -= 2.0f;
                        return 2 + base.update(elapsedTime);
                    }
                    break;
                default:
                    vel.X = -75.0f;
                    vel.Y = 0.0f;
                    break;
            }
            return base.update(elapsedTime);
        }
    }

    public class Boss : Enemy
    {
        public int state;
        public double stateTimer;
        public Boss( Texture2D tex, Vector2 screen) : base(tex, screen) 
        {
            state = 0;
            stateTimer = 0;
        }

        public override int update(double elapsedTime)
        {
            fireTimer += elapsedTime;
            stateTimer += elapsedTime;
            switch (flightPath)
            {
                case 0:
                    if (stateTimer >= 3.5)
                    {
                        state = (state + 1) % 2;
                        stateTimer -= 2.5;
                    }
                    if (state == 0)
                        vel.Y = -25.0f;
                    else
                        vel.Y = 25.0f;
                    if (fireTimer >= 1.0)
                    {
                        fireTimer -= 1.0;
                        pos += vel * (float)elapsedTime;
                        return 1;
                    }
                    pos += vel * (float)elapsedTime;
                    return 0;
                case 1:
                    if (stateTimer >= 3.5)
                    {
                        state = (state + 1) % 4;
                        stateTimer -= 3.5;
                    }
                    if (state < 2)
                        vel.Y = -15.0f;
                    else
                        vel.Y = 15.0f;
                    if (state % 2 == 0 && fireTimer >= 1.25)
                    {
                        fireTimer -= 1.15;
                        pos += vel * (float)elapsedTime;
                        return 2;
                    }
                    if (state % 2 == 1 && fireTimer >= 2.5)
                    {
                        fireTimer -= 1.0;
                        pos += vel * (float)elapsedTime;
                        return 3;
                    }
                    break;
                case 2:
                    angle = Math.PI / 6 * Math.Sin(stateTimer);
                    if( stateTimer >= 2 * Math.PI)
                        stateTimer -= 2 * Math.PI;
                    if (fireTimer >= 1.33)
                    {
                        pos += vel * (float)elapsedTime;
                        fireTimer -= 1.33;
                        return 4;
                    }
                    break;
            }
            pos += vel * (float)elapsedTime;
            return 0;
        }
    }

    public class Bullet : Sprite
    {
        public int damage;
        public Bullet( Texture2D tex, Vector2 screen) : base(tex, screen) 
        {
            damage = 1;
        }

        public Bullet(Texture2D tex, Vector2 screen, int dmg) : base(tex, screen)
        {
            damage = dmg;
        }
    }

    public class Background : Sprite
    {
        public Background(Vector2 screen) : base(screen) { }
        public Background(Texture2D tex, Vector2 screen) : base(tex, screen) 
        {
            pos.Y = screen.Y / 2.0f;
            vel = new Vector2(-75.0f, 0.0f);
        }

        public override int update(double elapsedTime)
        {
            if (pos.X >= SCREEN_SIZE.X)
                pos.X -= SCREEN_SIZE.X;
            if (pos.X <= -SCREEN_SIZE.X / 2.0f)
                pos.X += SCREEN_SIZE.X;
            return base.update(elapsedTime);
        }

        public override void draw(SpriteBatch sb)
        {
            sb.Draw(sprite, pos - new Vector2( size.X, 0.0f ), new Rectangle(0, 0, (int)size.X, (int)size.Y),
                    Color.White, (float)angle, origin, 1.0f, SpriteEffects.None, 1.0f);
            sb.Draw(sprite, pos, new Rectangle(0, 0, (int)size.X, (int)size.Y),
                    Color.White, (float)angle, origin, 1.0f, SpriteEffects.None, 1.0f);
            sb.Draw(sprite, pos + new Vector2(size.X, 0.0f), new Rectangle(0, 0, (int)size.X, (int)size.Y),
                    Color.White, (float)angle, origin, 1.0f, SpriteEffects.None, 1.0f);
        }
    }

    public class Powerup : Sprite
    {
        public int powerup_type; //0 = none; 1 = spread shot; 2 = rapid fire; 3 = tripleshot
        public double spawn_time;

        public Powerup(Texture2D tex, Vector2 screen, Vector2 start_pos)//constructor
            : base(tex, screen)
        {
            sprite = tex;
            powerup_type = 1;
            alive = false;
            pos = start_pos;
            vel = new Vector2(-5.0f, 0.0f);

        }
        public Powerup(Texture2D tex, Vector2 screen, Vector2 start_pos, double t)//constructor
            : base(tex, screen)
        {
            sprite = tex;
            spawn_time = t;
            powerup_type = 1;
            pos = start_pos;
            alive = false;
            vel = new Vector2(-5.0f, 0.0f);
        }

        public Powerup(Texture2D tex, Vector2 screen, double t, int type, Vector2 start_pos)//constructor
            : base(tex, screen)
        {
            sprite = tex;
            spawn_time = t;
            powerup_type = type;
            alive = false;
            pos = start_pos;
            vel = new Vector2(-5.0f, 0.0f);
        }

        public Powerup(Texture2D tex, Vector2 screen, Vector2 start_pos, int type)//constructor
            : base(tex, screen)
        {
            sprite = tex;
            powerup_type = type;
            pos = start_pos;
            alive = false;
            vel = new Vector2(-5.0f, 0.0f);
        }

        public override int update(double elapsedTime)
        {
            if (alive)
                pos += vel;

            return base.update(elapsedTime);
        }
    }

}
